Skip to content

CS-10618: Implement bidirectional realm sync command#4419

Merged
FadhlanR merged 8 commits intomainfrom
cs-10618-reimplement-boxel-realm-sync-command
Apr 17, 2026
Merged

CS-10618: Implement bidirectional realm sync command#4419
FadhlanR merged 8 commits intomainfrom
cs-10618-reimplement-boxel-realm-sync-command

Conversation

@FadhlanR
Copy link
Copy Markdown
Contributor

Summary

  • Add boxel realm sync command for bidirectional syncing between a local directory and a Boxel realm
  • Uses manifest-based change detection to classify files as changed/added/deleted on both local and remote sides
  • Supports conflict resolution strategies: --prefer-local, --prefer-remote, --prefer-newest
  • Supports --delete flag to sync deletions and --dry-run for previewing changes
  • Extracts shared sync-manifest utilities (SyncManifest, computeFileHash, loadManifest, saveManifest, pathExists) from push command into sync-manifest.ts
  • Includes integration tests

Test plan

  • Run realm sync integration tests
  • Manual test: sync a local directory with a realm (both directions)
  • Verify conflict resolution with --prefer-local, --prefer-remote, --prefer-newest
  • Verify --dry-run previews without making changes
  • Verify existing realm push still works after manifest extraction

🤖 Generated with Claude Code

FadhlanR and others added 3 commits April 16, 2026 16:25
Add `boxel realm sync` for bidirectional syncing between a local directory
and a Boxel realm. Uses a manifest-based approach to detect changes on both
sides, with conflict resolution strategies (--prefer-local, --prefer-remote,
--prefer-newest). Extracts shared sync-manifest utilities from push command.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Use 'update' instead of 'add' in atomic upload when file exists on
  remote but no manifest is present (first sync with overlapping files)
- Add 1s delay in establishBaseline to avoid same-second mtime collisions
- Account for realm-default index.json in manifest assertions
- Stabilize no-op test by syncing once before checking for no checkpoint

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@FadhlanR FadhlanR marked this pull request as ready for review April 16, 2026 10:34
@FadhlanR FadhlanR requested a review from a team April 16, 2026 10:34
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4d0c7f70a8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +253 to +257
if (totalOps === 0) {
console.log('\nEverything is up to date');
if (!this.options.dryRun && !effectiveManifest) {
// First sync with no changes needed - still write manifest
await this.writeManifest(localHashes, remoteMtimes);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Block manifest writes when conflicts are skipped

When conflicts are skipped (no --prefer-* strategy), totalOps can still be 0, so this branch reports “Everything is up to date” and writes a first-sync manifest. In that scenario, conflicting local/remote versions get recorded as if they were synchronized, so later runs classify them as unchanged and never surface the conflict again. Skip-success paths should not write a manifest while skippedConflicts is non-empty.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in da77e80 — added a skippedConflicts.length === 0 guard to the manifest write condition, so first-sync manifests are only written when there are no unresolved conflicts.

Comment thread packages/boxel-cli/src/commands/realm/sync.ts Outdated
Comment thread packages/boxel-cli/src/commands/realm/sync.ts Outdated
@habdelra habdelra requested a review from Copilot April 16, 2026 14:11
Comment thread docs/cs-10618-realm-sync-plan.md Outdated
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new boxel realm sync command to support bidirectional syncing between a local directory and a Boxel realm using a persisted sync manifest, with conflict-resolution flags and integration coverage.

Changes:

  • Introduce realm sync command implementing bidirectional classification (push/pull/deletes/conflicts) and checkpointing.
  • Extract shared manifest helpers into src/lib/sync-manifest.ts and refactor realm push to use them.
  • Add integration tests covering bidirectional sync, conflicts, deletion behavior, dry-run, and manifest updates.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
packages/boxel-cli/src/commands/realm/sync.ts New bidirectional sync command + conflict strategies, manifest update, and checkpoint creation.
packages/boxel-cli/src/lib/sync-manifest.ts New shared utilities for reading/writing the .boxel-sync.json manifest and hashing files.
packages/boxel-cli/src/commands/realm/push.ts Refactor to use shared sync-manifest utilities instead of inline implementations.
packages/boxel-cli/src/commands/realm/index.ts Register the new sync subcommand under boxel realm.
packages/boxel-cli/tests/integration/realm-sync.test.ts New integration test suite for sync behavior and conflict/deletion scenarios.
docs/cs-10618-realm-sync-plan.md Design/plan document for the new sync command and test coverage.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/boxel-cli/src/commands/realm/sync.ts Outdated
Comment thread packages/boxel-cli/src/commands/realm/sync.ts Outdated
Comment thread packages/boxel-cli/src/commands/realm/sync.ts Outdated
Comment thread packages/boxel-cli/src/commands/realm/sync.ts
Comment thread packages/boxel-cli/src/commands/realm/sync.ts Outdated
FadhlanR and others added 2 commits April 17, 2026 12:23
Remove plan doc from repo per reviewer feedback. Fix manifest being
written on first sync when conflicts are skipped (which would silently
lose conflict state). Add explicit conflict handling for cross-state
combinations (changed+added) that previously fell through to noop.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@FadhlanR FadhlanR requested a review from habdelra April 17, 2026 05:37
FadhlanR and others added 3 commits April 17, 2026 13:27
…etions

Fall back to getRemoteFileList when _mtimes endpoint is unavailable so
remote-only files remain visible for pulling. Preserve manifest entries
for files deleted locally but not propagated (no --delete), preventing
unexpected re-pulls on the next sync.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…t remoteMtimes

Derive localFiles from getLocalFileListWithMtimes() instead of calling
both getLocalFileList() and getLocalFileListWithMtimes() separately.

Use manifest.files as a secondary known-paths set in classifyRemote()
so files known from a previous sync but lacking remoteMtimes are
classified as 'changed' instead of 'added', avoiding false conflicts.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Move classifyLocal, classifyRemote, determineAction, and resolveConflict
from private RealmSyncer methods into src/lib/sync-logic.ts as pure
exported functions. Add 42 unit tests covering classification, action
matrix (including cross-state conflicts fix), deletion semantics, and
all conflict resolution strategies.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@FadhlanR FadhlanR merged commit 2d964eb into main Apr 17, 2026
22 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants